package com.gframework.sqlparam;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;

import org.springframework.util.StringUtils;

import com.gframework.util.GStringUtils;


/**
 * 参数判断模式操作抽象类.
 *
 * @since 1.0.0
 * @author Ghwolf
 */
abstract class AbstractParamModeHandler implements ParamModeHandler {

	/**
	 * 列名称
	 */
	protected String column;
	/**
	 * 执行操作的方法
	 */
	private Member member;
	/**
	 * 是否时方法，不是方法就是属性
	 */
	private boolean isMethod ;

	/**
	 * 忽略的方法名称前缀
	 */
	private static final String IGNORE_METHOD_PREFIX = "get";

	/**
	 * 实例化本类对象，需要提供 {@link ParamMode} 注解对象，和注解成员对象
	 * 
	 * @param paramMode {@link ParamMode}注解对象
	 * @param member 方法或属性成员对象
	 * @throws ParamModeException 配置错误，例如方法返回值和配置条件类型不匹配
	 */
	protected AbstractParamModeHandler(ParamMode paramMode, Member member) {
		// 解除封装和合法性验证
		if (member instanceof AccessibleObject) {
			((AccessibleObject) member).setAccessible(true);
		}
		if (member instanceof Method) {
			this.assertMethod(paramMode, (Method) member);
			this.isMethod = true ;
		} else {
			this.assertField(paramMode, (Field) member);
		}
		
		String column = paramMode.column();
		if (StringUtils.isEmpty(column)) {
			String name ;
			if (this.isMethod) {
				name = ((Method)member).getName();
				if (name.startsWith(IGNORE_METHOD_PREFIX)) {
					name = name.substring(IGNORE_METHOD_PREFIX.length());
				}
			} else {
				name = ((Field)member).getName();
			}
			column = GStringUtils.toMapUnderscore(name);
		}
		this.column = column;
		this.member = member ;
	}

	/**
	 * 验证方法是否符合规范
	 */
	private void assertMethod(ParamMode paramMode, Method method) {
		if (method.getParameterCount() != 0) {
			throw new ParamModeException("方法[" + this.getMemberFullName(method) + "]有参数，不能用于ParamMode注解！");
		}
		if (!this.validateReturnType(paramMode, method.getReturnType())) {
			throw new ParamModeException("方法[" + this.getMemberFullName(method) + "]的返回值类型不支持此注解！");
		}
	}
	
	/**
	 * 验证属性是否符合规范
	 */
	private void assertField(ParamMode paramMode, Field field) {
		if (!this.validateReturnType(paramMode, field.getType())) {
			throw new ParamModeException("属性[" + this.getMemberFullName(field) + "]的返回值类型不支持此注解！");
		}
	}

	/**
	 * 取得成员路径全名
	 */
	private String getMemberFullName(Member member) {
		if (member instanceof Method) {
			Method method = (Method) member ;
			return method.getDeclaringClass().getName() + "# (" + method.getReturnType() + ") " + method.getName();
		} else {
			Field field = (Field) member ;
			return field.getDeclaringClass().getName() + "# (" + field.getType() + ")" + field.getName();
			
		}
	}

	@Override
	public void doSet(SqlParam sp, Object obj)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Object value ;
		if (this.isMethod) {
			value = ((Method)this.member).invoke(obj);
		} else {
			value = ((Field)this.member).get(obj);
		}
		if (value != null) {
			this.handle(sp, value);
		}
	}

	/**
	 * 处理操作，如果value为null，则不做任何事情
	 * 
	 * @param sp SqlParam类实例化对象
	 * @param value 参数值，为null则不做任何事情
	 */
	public abstract void handle(SqlParam sp, Object value);

	/**
	 * 子类根据需要复写此方法，用于验证该注解所进行的操作是否支持此方法返回值类型
	 * 
	 * @param returnType 方法返回值类型的class类对象
	 * @return 如果支持返回true，false表示不支持
	 */
	protected boolean validateReturnType(ParamMode paramMode, Class<?> returnType) {
		return true;
	}

	/**
	 * 取得可以获取此参数的方法对象
	 */
	public Member getMember() {
		return this.member;
	}

	@Override
	public String toString() {
		return this.getMemberFullName(this.getMember());
	}
	
	protected boolean isMethod() {
		return this.isMethod;
	}
	
}
